import { checkBrowser } from './playerWasm/public.js'

const loadScript = (src, callback) => {
  const dom = document.createElement('script')
  dom.src = src
  document.head.appendChild(dom)
  var loaded = false
  if (typeof callback === 'function') {
    dom.onload = dom.onreadystatechange = function () {
      if (!loaded && (!dom.readyState || /loaded|complete/.test(dom.readyState))) {
        dom.onload = dom.onreadystatechange = null
        loaded = true
        callback()
      }
    }
  }
}

const isInclude = (name) => {
  var js = /js$/i.test(name)
  var es = document.getElementsByTagName(js ? 'script' : 'link')
  for (var i = 0; i < es.length; i++) {
    if (es[i][js ? 'src' : 'href'].indexOf(name) != -1) return true
  }
  return false
}

var m_bClientInitialized = false
var m_nModuleInitialized = false

// Load multi-threaded PlaySDK library files
const loadLibPlaySDK = () => {
  const libPath = 'WasmLib/MultiThread/libplay.js'
  if (!isInclude('MultiThread/libplay.js')) {
    loadScript(libPath, null)
  }
}

// Load multi-threaded streaming library files
const loadMultiStreamClient = () => {
  let libPath = 'WasmLib/MultiThread/libStreamClient.js'
  if (!isInclude('MultiThread/libStreamClient.js')) {
    loadScript(libPath, () => {
      //Initialize Streaming Module
      Multi_Client_Module().then((instance) => {
        window.SCModule = instance
        //Multiple channels only need to be initialized once
        window.SCModule._GLOBAL_Init()
        m_bClientInitialized = true
      })
    })
  }
}

// Load rendering engine files
const loadLibRenderEngine = () => {
  let libPath = 'WasmLib/Common/libRenderEngine.js'
  if (!isInclude('Common/libRenderEngine.js')) {
    loadScript(libPath, () => {
      //Initialize rendering engine module
      RenderEngine_Module().then((instance) => {
        window.REModule = instance
      })
    })
  }
}

// Load IVSdrawer file
const loadLibIVSDrawer = () => {
  let libPath = 'WasmLib/Common/libIVSDrawer.js'
  if (!isInclude('Common/libIVSDrawer.js')) {
    loadScript(libPath, () => {
      // Initialize IVSDrawer Module
      IVSDrawer_Module().then((instance) => {
        window.IVSModule = instance
      })
    })
  }
}

// Load 3A (ANC echo cancellation+AEC noise reduction+AGC gain) algorithm library
const loadLibASPLite = () => {
  let libPath = 'WasmLib/Common/libmavasp_litepacket.js'
  let dataPath = 'WasmLib/Common/libmavasp_litepacket.data'
  if (!isInclude('Common/libmavasp_litepacket.js')) {
    loadScript(libPath, () => {
      //Set the path to the libmavasp_litepacket.data configuration file
      ASPLite_Module['locateFile'] = function (path, prefix) {
        if (path.endsWith('.data')) {
          return dataPath
        }
        return prefix + path
      }
      //Initialize ASPLite Module
      ASPLite_Module(ASPLite_Module).then((instance) => {
        window.ASPLiteModule = instance
      })
    })
  }
}

if (!window.Module) {
  window.Module = {}
}

// PlaySDK wasm module loading and initialization completed callback
Module.onRuntimeInitialized = function () {
  m_nModuleInitialized = true
}
// Reset font file path
Module.locateFile = function (path, prefix) {
  if (path.endsWith('.data')) {
    return 'WasmLib/MultiThread/libplay.data'
  }
  return prefix + path
}

const loadLibDHPlay = (bSupportMultiThread) => {
  if (bSupportMultiThread) {
    loadLibPlaySDK()
    //Multi threaded version, using StreamClient wasm module for streaming
    loadMultiStreamClient()
  }

  if (!bSupportMultiThread) {
    loadLibRenderEngine()
  }
  loadLibIVSDrawer()
  //Load 3A algorithm library
  loadLibASPLite()

  let timer = null
  return new Promise((resolve) => {
    timer = setInterval(() => {
      if (!bSupportMultiThread || (m_nModuleInitialized && m_bClientInitialized)) {
        clearInterval(timer)
        resolve()
      }
    })
  })
}

const init = async (players, runtimeInitializedCallBack) => {
  if (!players) return
  if (!Array.isArray(players)) players = [players]
  let cPlusVisibleDecCallBack = () => {}
  let cDigitalSignCallBack = () => {}
  let cExtraDrawDataCallBack = () => {}
  let cExtraDrawDrawCallBack = () => {}
  let cRecordDataCallBack = () => {}
  let cIVSDrawDataCallBack = () => {}
  if (players.length) {
    //Stream data callback, passed from C++to the JavaScript layer
    window.cPlusMediaFrameCallBack = function (handle, index, pData, nDataLength) {
      players.forEach((player) => {
        player.inputData(handle, pData, nDataLength)
      })
    }

    //RTSP message callback, passed from C++to the JavaScript layer
    window.cPlusRtspMsgCallBack = function (handle, type, attach) {
      players.forEach((player) => {
        switch (type) {
          //The module responsible for handling the RTSP protocol flow, if it fails during RTSP processing, sends the message to the message receiver
          //Upon receiving this message, the recipient should call Close() to close the client. The attached parameter attach to the message is not used
          case 0x1000:
            player.streamFailedCallback(handle, attach)
            player.stopPullStream(handle)
            player.streamDisconnectCallback(handle)
            break
          //Indicates that the client has completed the initialization and setup steps for the peer, and at this point, the user can obtain the SDP,
          //The attached parameter attach to the message is not used.
          //RTSP_PutStream can only be called after receiving this message in intercom services
          case 0x1001:
            break
          //RTSP_PutStream只有在对讲机服务中收到此消息后才能调用
          //The attached parameter attach to the message is not used.
          case 0x1004:
            player.streamFinishCallback(handle)
            break
          // RTSP redirect message, when the client has configured clientConfigRedirDisable to enable, it will notify the upper layer of the message,
          // Implement redirection function from the upper layer
          case 0x1008:
            player.streamRedirectCallback(handle, attach)
            break
        }
      })
    }

    //RTSV message callback, passed from C++to the JS layer
    window.cPlusRtsvMsgCallBack = function (handle, type) {
      players.forEach((player) => {
        //Find the corresponding streaming session based on the handle
        switch (type) {
          case 0x4001:
            //Only after receiving this message in the intercom business can RTSV-PutStream be called
            break
          case 0x4002:
            player.streamFinishCallback(handle)
            break
        }
      })
    }
    cPlusVisibleDecCallBack = (nPort, pBufY, pBufU, pBufV, nSize, pFrameInfo) => {
      players.forEach((player) => {
        // Multiple windows require executing corresponding methods for multiple window objects
        player && player.cPlusVisibleDecCallBack(nPort, pBufY, pBufU, pBufV, nSize, pFrameInfo)
      })
    }
    cDigitalSignCallBack = (nPort, nFrameID, bSuccess) => {
      players.forEach((player) => {
        player && player.cDigitalSignCallBack(nPort, nFrameID, bSuccess)
      })
    }
    cExtraDrawDataCallBack = (nPort, nDataType, pDrawData, nDataLen) => {
      players.forEach((player) => {
        player && player.cExtraDrawDataCallBack(nPort, nDataType, pDrawData, nDataLen)
      })
    }
    cExtraDrawDrawCallBack = (nPort) => {
      players.forEach((player) => {
        player && player.cExtraDrawDrawCallBack(nPort)
      })
    }
    cRecordDataCallBack = (nPort, pData, nDataLen, nOffset, pRecordFrameInfo) => {
      players.forEach((player) => {
        player && player.cRecordDataCallBack(nPort, pData, nDataLen, nOffset, pRecordFrameInfo)
      })
    }
    //External TODO, such as device location information in 360AR, is returned through this callback and written to the device by the upper layer
    cIVSDrawDataCallBack = (nPort, buf, type, len, reallen) => {
      players.forEach((player) => {
        player && player.cIVSDrawDataCallBack(nPort, buf, type, len, reallen)
      })
    }
  }
  window.cPlusVisibleDecCallBack = cPlusVisibleDecCallBack

  window.cDigitalSignCallBack = cDigitalSignCallBack

  window.cExtraDrawDataCallBack = cExtraDrawDataCallBack

  window.cExtraDrawDrawCallBack = cExtraDrawDrawCallBack

  window.cRecordDataCallBack = cRecordDataCallBack

  window.cIVSDrawDataCallBack = cIVSDrawDataCallBack

  Module.onRuntimeInitialized = function () {
    runtimeInitializedCallBack()
  }

  const { bSupportMultiThread } = checkBrowser()
  if (!isInclude('/libplay.js')) {
    await loadLibDHPlay(bSupportMultiThread)
  }
}

export default { init }
